1 /*
2 * Copyright (C) 2008 The Guava Authors
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package com.google.common.primitives;
18
19 import static com.google.common.base.Preconditions.checkArgument;
20 import static com.google.common.base.Preconditions.checkElementIndex;
21 import static com.google.common.base.Preconditions.checkNotNull;
22 import static com.google.common.base.Preconditions.checkPositionIndexes;
23
24 import com.google.common.annotations.GwtCompatible;
25
26 import java.io.Serializable;
27 import java.util.AbstractList;
28 import java.util.Collection;
29 import java.util.Collections;
30 import java.util.Comparator;
31 import java.util.List;
32 import java.util.RandomAccess;
33
34 /**
35 * Static utility methods pertaining to {@code char} primitives, that are not
36 * already found in either {@link Character} or {@link Arrays}.
37 *
38 * <p>All the operations in this class treat {@code char} values strictly
39 * numerically; they are neither Unicode-aware nor locale-dependent.
40 *
41 * <p>See the Guava User Guide article on <a href=
42 * "http://code.google.com/p/guava-libraries/wiki/PrimitivesExplained">
43 * primitive utilities</a>.
44 *
45 * @author Kevin Bourrillion
46 * @since 1.0
47 */
48 @GwtCompatible(emulated = true)
49 public final class Chars {
50 private Chars() {}
51
52 /**
53 * The number of bytes required to represent a primitive {@code char}
54 * value.
55 */
56 public static final int BYTES = Character.SIZE / Byte.SIZE;
57
58 /**
59 * Returns a hash code for {@code value}; equal to the result of invoking
60 * {@code ((Character) value).hashCode()}.
61 *
62 * @param value a primitive {@code char} value
63 * @return a hash code for the value
64 */
65 public static int hashCode(char value) {
66 return value;
67 }
68
69 /**
70 * Returns the {@code char} value that is equal to {@code value}, if possible.
71 *
72 * @param value any value in the range of the {@code char} type
73 * @return the {@code char} value that equals {@code value}
74 * @throws IllegalArgumentException if {@code value} is greater than {@link
75 * Character#MAX_VALUE} or less than {@link Character#MIN_VALUE}
76 */
77 public static char checkedCast(long value) {
78 char result = (char) value;
79 if (result != value) {
80 // don't use checkArgument here, to avoid boxing
81 throw new IllegalArgumentException("Out of range: " + value);
82 }
83 return result;
84 }
85
86 /**
87 * Returns the {@code char} nearest in value to {@code value}.
88 *
89 * @param value any {@code long} value
90 * @return the same value cast to {@code char} if it is in the range of the
91 * {@code char} type, {@link Character#MAX_VALUE} if it is too large,
92 * or {@link Character#MIN_VALUE} if it is too small
93 */
94 public static char saturatedCast(long value) {
95 if (value > Character.MAX_VALUE) {
96 return Character.MAX_VALUE;
97 }
98 if (value < Character.MIN_VALUE) {
99 return Character.MIN_VALUE;
100 }
101 return (char) value;
102 }
103
104 /**
105 * Compares the two specified {@code char} values. The sign of the value
106 * returned is the same as that of {@code ((Character) a).compareTo(b)}.
107 *
108 * <p><b>Note for Java 7 and later:</b> this method should be treated as
109 * deprecated; use the equivalent {@link Character#compare} method instead.
110 *
111 * @param a the first {@code char} to compare
112 * @param b the second {@code char} to compare
113 * @return a negative value if {@code a} is less than {@code b}; a positive
114 * value if {@code a} is greater than {@code b}; or zero if they are equal
115 */
116 public static int compare(char a, char b) {
117 return a - b; // safe due to restricted range
118 }
119
120 /**
121 * Returns {@code true} if {@code target} is present as an element anywhere in
122 * {@code array}.
123 *
124 * @param array an array of {@code char} values, possibly empty
125 * @param target a primitive {@code char} value
126 * @return {@code true} if {@code array[i] == target} for some value of {@code
127 * i}
128 */
129 public static boolean contains(char[] array, char target) {
130 for (char value : array) {
131 if (value == target) {
132 return true;
133 }
134 }
135 return false;
136 }
137
138 /**
139 * Returns the index of the first appearance of the value {@code target} in
140 * {@code array}.
141 *
142 * @param array an array of {@code char} values, possibly empty
143 * @param target a primitive {@code char} value
144 * @return the least index {@code i} for which {@code array[i] == target}, or
145 * {@code -1} if no such index exists.
146 */
147 public static int indexOf(char[] array, char target) {
148 return indexOf(array, target, 0, array.length);
149 }
150
151 // TODO(kevinb): consider making this public
152 private static int indexOf(
153 char[] array, char target, int start, int end) {
154 for (int i = start; i < end; i++) {
155 if (array[i] == target) {
156 return i;
157 }
158 }
159 return -1;
160 }
161
162 /**
163 * Returns the start position of the first occurrence of the specified {@code
164 * target} within {@code array}, or {@code -1} if there is no such occurrence.
165 *
166 * <p>More formally, returns the lowest index {@code i} such that {@code
167 * java.util.Arrays.copyOfRange(array, i, i + target.length)} contains exactly
168 * the same elements as {@code target}.
169 *
170 * @param array the array to search for the sequence {@code target}
171 * @param target the array to search for as a sub-sequence of {@code array}
172 */
173 public static int indexOf(char[] array, char[] target) {
174 checkNotNull(array, "array");
175 checkNotNull(target, "target");
176 if (target.length == 0) {
177 return 0;
178 }
179
180 outer:
181 for (int i = 0; i < array.length - target.length + 1; i++) {
182 for (int j = 0; j < target.length; j++) {
183 if (array[i + j] != target[j]) {
184 continue outer;
185 }
186 }
187 return i;
188 }
189 return -1;
190 }
191
192 /**
193 * Returns the index of the last appearance of the value {@code target} in
194 * {@code array}.
195 *
196 * @param array an array of {@code char} values, possibly empty
197 * @param target a primitive {@code char} value
198 * @return the greatest index {@code i} for which {@code array[i] == target},
199 * or {@code -1} if no such index exists.
200 */
201 public static int lastIndexOf(char[] array, char target) {
202 return lastIndexOf(array, target, 0, array.length);
203 }
204
205 // TODO(kevinb): consider making this public
206 private static int lastIndexOf(
207 char[] array, char target, int start, int end) {
208 for (int i = end - 1; i >= start; i--) {
209 if (array[i] == target) {
210 return i;
211 }
212 }
213 return -1;
214 }
215
216 /**
217 * Returns the least value present in {@code array}.
218 *
219 * @param array a <i>nonempty</i> array of {@code char} values
220 * @return the value present in {@code array} that is less than or equal to
221 * every other value in the array
222 * @throws IllegalArgumentException if {@code array} is empty
223 */
224 public static char min(char... array) {
225 checkArgument(array.length > 0);
226 char min = array[0];
227 for (int i = 1; i < array.length; i++) {
228 if (array[i] < min) {
229 min = array[i];
230 }
231 }
232 return min;
233 }
234
235 /**
236 * Returns the greatest value present in {@code array}.
237 *
238 * @param array a <i>nonempty</i> array of {@code char} values
239 * @return the value present in {@code array} that is greater than or equal to
240 * every other value in the array
241 * @throws IllegalArgumentException if {@code array} is empty
242 */
243 public static char max(char... array) {
244 checkArgument(array.length > 0);
245 char max = array[0];
246 for (int i = 1; i < array.length; i++) {
247 if (array[i] > max) {
248 max = array[i];
249 }
250 }
251 return max;
252 }
253
254 /**
255 * Returns the values from each provided array combined into a single array.
256 * For example, {@code concat(new char[] {a, b}, new char[] {}, new
257 * char[] {c}} returns the array {@code {a, b, c}}.
258 *
259 * @param arrays zero or more {@code char} arrays
260 * @return a single array containing all the values from the source arrays, in
261 * order
262 */
263 public static char[] concat(char[]... arrays) {
264 int length = 0;
265 for (char[] array : arrays) {
266 length += array.length;
267 }
268 char[] result = new char[length];
269 int pos = 0;
270 for (char[] array : arrays) {
271 System.arraycopy(array, 0, result, pos, array.length);
272 pos += array.length;
273 }
274 return result;
275 }
276
277 /**
278 * Returns an array containing the same values as {@code array}, but
279 * guaranteed to be of a specified minimum length. If {@code array} already
280 * has a length of at least {@code minLength}, it is returned directly.
281 * Otherwise, a new array of size {@code minLength + padding} is returned,
282 * containing the values of {@code array}, and zeroes in the remaining places.
283 *
284 * @param array the source array
285 * @param minLength the minimum length the returned array must guarantee
286 * @param padding an extra amount to "grow" the array by if growth is
287 * necessary
288 * @throws IllegalArgumentException if {@code minLength} or {@code padding} is
289 * negative
290 * @return an array containing the values of {@code array}, with guaranteed
291 * minimum length {@code minLength}
292 */
293 public static char[] ensureCapacity(
294 char[] array, int minLength, int padding) {
295 checkArgument(minLength >= 0, "Invalid minLength: %s", minLength);
296 checkArgument(padding >= 0, "Invalid padding: %s", padding);
297 return (array.length < minLength)
298 ? copyOf(array, minLength + padding)
299 : array;
300 }
301
302 // Arrays.copyOf() requires Java 6
303 private static char[] copyOf(char[] original, int length) {
304 char[] copy = new char[length];
305 System.arraycopy(original, 0, copy, 0, Math.min(original.length, length));
306 return copy;
307 }
308
309 /**
310 * Returns a string containing the supplied {@code char} values separated
311 * by {@code separator}. For example, {@code join("-", '1', '2', '3')} returns
312 * the string {@code "1-2-3"}.
313 *
314 * @param separator the text that should appear between consecutive values in
315 * the resulting string (but not at the start or end)
316 * @param array an array of {@code char} values, possibly empty
317 */
318 public static String join(String separator, char... array) {
319 checkNotNull(separator);
320 int len = array.length;
321 if (len == 0) {
322 return "";
323 }
324
325 StringBuilder builder
326 = new StringBuilder(len + separator.length() * (len - 1));
327 builder.append(array[0]);
328 for (int i = 1; i < len; i++) {
329 builder.append(separator).append(array[i]);
330 }
331 return builder.toString();
332 }
333
334 /**
335 * Returns a comparator that compares two {@code char} arrays
336 * lexicographically. That is, it compares, using {@link
337 * #compare(char, char)}), the first pair of values that follow any
338 * common prefix, or when one array is a prefix of the other, treats the
339 * shorter array as the lesser. For example,
340 * {@code [] < ['a'] < ['a', 'b'] < ['b']}.
341 *
342 * <p>The returned comparator is inconsistent with {@link
343 * Object#equals(Object)} (since arrays support only identity equality), but
344 * it is consistent with {@link Arrays#equals(char[], char[])}.
345 *
346 * @see <a href="http://en.wikipedia.org/wiki/Lexicographical_order">
347 * Lexicographical order article at Wikipedia</a>
348 * @since 2.0
349 */
350 public static Comparator<char[]> lexicographicalComparator() {
351 return LexicographicalComparator.INSTANCE;
352 }
353
354 private enum LexicographicalComparator implements Comparator<char[]> {
355 INSTANCE;
356
357 @Override
358 public int compare(char[] left, char[] right) {
359 int minLength = Math.min(left.length, right.length);
360 for (int i = 0; i < minLength; i++) {
361 int result = Chars.compare(left[i], right[i]);
362 if (result != 0) {
363 return result;
364 }
365 }
366 return left.length - right.length;
367 }
368 }
369
370 /**
371 * Copies a collection of {@code Character} instances into a new array of
372 * primitive {@code char} values.
373 *
374 * <p>Elements are copied from the argument collection as if by {@code
375 * collection.toArray()}. Calling this method is as thread-safe as calling
376 * that method.
377 *
378 * @param collection a collection of {@code Character} objects
379 * @return an array containing the same values as {@code collection}, in the
380 * same order, converted to primitives
381 * @throws NullPointerException if {@code collection} or any of its elements
382 * is null
383 */
384 public static char[] toArray(Collection<Character> collection) {
385 if (collection instanceof CharArrayAsList) {
386 return ((CharArrayAsList) collection).toCharArray();
387 }
388
389 Object[] boxedArray = collection.toArray();
390 int len = boxedArray.length;
391 char[] array = new char[len];
392 for (int i = 0; i < len; i++) {
393 // checkNotNull for GWT (do not optimize)
394 array[i] = (Character) checkNotNull(boxedArray[i]);
395 }
396 return array;
397 }
398
399 /**
400 * Returns a fixed-size list backed by the specified array, similar to {@link
401 * Arrays#asList(Object[])}. The list supports {@link List#set(int, Object)},
402 * but any attempt to set a value to {@code null} will result in a {@link
403 * NullPointerException}.
404 *
405 * <p>The returned list maintains the values, but not the identities, of
406 * {@code Character} objects written to or read from it. For example, whether
407 * {@code list.get(0) == list.get(0)} is true for the returned list is
408 * unspecified.
409 *
410 * @param backingArray the array to back the list
411 * @return a list view of the array
412 */
413 public static List<Character> asList(char... backingArray) {
414 if (backingArray.length == 0) {
415 return Collections.emptyList();
416 }
417 return new CharArrayAsList(backingArray);
418 }
419
420 @GwtCompatible
421 private static class CharArrayAsList extends AbstractList<Character>
422 implements RandomAccess, Serializable {
423 final char[] array;
424 final int start;
425 final int end;
426
427 CharArrayAsList(char[] array) {
428 this(array, 0, array.length);
429 }
430
431 CharArrayAsList(char[] array, int start, int end) {
432 this.array = array;
433 this.start = start;
434 this.end = end;
435 }
436
437 @Override public int size() {
438 return end - start;
439 }
440
441 @Override public boolean isEmpty() {
442 return false;
443 }
444
445 @Override public Character get(int index) {
446 checkElementIndex(index, size());
447 return array[start + index];
448 }
449
450 @Override public boolean contains(Object target) {
451 // Overridden to prevent a ton of boxing
452 return (target instanceof Character)
453 && Chars.indexOf(array, (Character) target, start, end) != -1;
454 }
455
456 @Override public int indexOf(Object target) {
457 // Overridden to prevent a ton of boxing
458 if (target instanceof Character) {
459 int i = Chars.indexOf(array, (Character) target, start, end);
460 if (i >= 0) {
461 return i - start;
462 }
463 }
464 return -1;
465 }
466
467 @Override public int lastIndexOf(Object target) {
468 // Overridden to prevent a ton of boxing
469 if (target instanceof Character) {
470 int i = Chars.lastIndexOf(array, (Character) target, start, end);
471 if (i >= 0) {
472 return i - start;
473 }
474 }
475 return -1;
476 }
477
478 @Override public Character set(int index, Character element) {
479 checkElementIndex(index, size());
480 char oldValue = array[start + index];
481 // checkNotNull for GWT (do not optimize)
482 array[start + index] = checkNotNull(element);
483 return oldValue;
484 }
485
486 @Override public List<Character> subList(int fromIndex, int toIndex) {
487 int size = size();
488 checkPositionIndexes(fromIndex, toIndex, size);
489 if (fromIndex == toIndex) {
490 return Collections.emptyList();
491 }
492 return new CharArrayAsList(array, start + fromIndex, start + toIndex);
493 }
494
495 @Override public boolean equals(Object object) {
496 if (object == this) {
497 return true;
498 }
499 if (object instanceof CharArrayAsList) {
500 CharArrayAsList that = (CharArrayAsList) object;
501 int size = size();
502 if (that.size() != size) {
503 return false;
504 }
505 for (int i = 0; i < size; i++) {
506 if (array[start + i] != that.array[that.start + i]) {
507 return false;
508 }
509 }
510 return true;
511 }
512 return super.equals(object);
513 }
514
515 @Override public int hashCode() {
516 int result = 1;
517 for (int i = start; i < end; i++) {
518 result = 31 * result + Chars.hashCode(array[i]);
519 }
520 return result;
521 }
522
523 @Override public String toString() {
524 StringBuilder builder = new StringBuilder(size() * 3);
525 builder.append('[').append(array[start]);
526 for (int i = start + 1; i < end; i++) {
527 builder.append(", ").append(array[i]);
528 }
529 return builder.append(']').toString();
530 }
531
532 char[] toCharArray() {
533 // Arrays.copyOfRange() is not available under GWT
534 int size = size();
535 char[] result = new char[size];
536 System.arraycopy(array, start, result, 0, size);
537 return result;
538 }
539
540 private static final long serialVersionUID = 0;
541 }
542 }